[bits 16]
[org 0x7c00]

cli
jmp load32

; GDT descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start

; GDT
gdt_start:
; Null descriptor
dd 0
dd 0

; Code descriptor
.Code: equ $ - gdt_start
dw 0xFFFF ; limit 0-15
dw 0 ; base 0-15
db 0 ; base 16-23

;Access Byte https://wiki.osdev.org/Global_Descriptor_Table#Segment_Descriptor
;7	6	5	4	3	2	1	0
;P	DPL		S	E	DC	RW	A
db 0x9B ; 10011011b

;Flags
;3	2	1	0
;G	DB	L	Reserved
db 0xCF ; limit 16-19 and flags 0-3 11001111b limit = 0xf
db 0 ; base 24-31

; Data descriptor
.Data: equ $ - gdt_start
dw 0xFFFF
dw 0
db 0
db 0x93 ; 10010011b
db 0xCF
db 0

gdt_end:

load32:
; Load the GDT
lgdt [gdt_descriptor]

; Set the PE bit in CR0
mov eax, cr0
or eax, 1
mov cr0, eax

; Jump to 32-bit code
jmp 0x08:BEGIN_32BIT

[bits 32]
BEGIN_32BIT:
; Set up the segment registers
mov ax, gdt_start.Data
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax

jmp load64

; GDT 64 bit descriptor
GDT64:
.Null: equ $ - GDT64
dq 0

.Code: equ $ - GDT64
dw 0, 0
; ; Limit, Base - low
db 0, 10011010b, 10101111b, 0
; ; Base - middle, Access(X/R),(Granularity |flag for 64 bit | limit 19 - 16), Base - high
; limit = 11110000000000000000 = 0xF0000 = 983040
; limit 983040 * 1024 * 4 = 4026531840 bytes segment size

.Data: equ $ - GDT64
dw 0,0
; Limit, Base - low                      
db 0, 10010010b, 10001111b, 0
; Base - middle, Access(R/W),Granularity, Base - high  

.Pointer:
dw $ - GDT64 - 1
; Limit
dq GDT64
; Base

load64:

; PML4T
mov edi, 0x3000
; Base addres of top page table set to 0x3000
mov cr3, edi


xor eax, eax
; set eax to 0

; x86 uses a page size of 4096 bytes
; Each table contains 512 entries
; Each entry is of 8 bytes
; 512 * 8 = 4096 bytes (the size of page table)
mov ecx, 4096 
rep stosd           
; Clear the memory.
mov eax, cr3

; DECLARING LAME PAGE TABLE AND CONNECTIONs
;  PML4T[0] -> PDPT[0] -> PDT[0] 0x4000-> PT[0] -> 2 MiB
;  0x3000 -> PML4T[0] -> 0x4003 
;  0x4000 -> PDT[0] -> 0x5003


; PDPT
mov dword [eax], 0x4000 | 3  ; 2nd top level page table base address
add eax, 4096  

; PDT           
mov dword [eax], 0x5000 | 3   ; 3rd top level page table base address
add eax, 4096   

; PT
mov dword [eax], 0x6000 | 3     ; page table base address
add eax, 4096             

; first page
mov ebx, 0x00000003          ; Set the B-register to 0x00000003.

mov ecx, 512

; Initialise Pages for first 2MiB
; At present eax points to base address of PT.
SetPageEntry:
mov dword [eax], ebx                   
add eax, 8      ; each page entry is of 8 bytes
add ebx, 0x1000  
loop SetPageEntry             


mov ecx, 0xC0000080          
rdmsr
; Read MSR specified by ECX into EDX:EAX.                   
or eax, 1 << 8               
wrmsr   

; Will set PAE bit (which the bit 5) in CR4
mov edi, cr4                
or edi, 1 << 5               
mov cr4, edi    

; Eanble PG-bit in CR0 which is the 31 bit
mov edi, cr0              
or edi, 1 << 31             
mov cr0, edi

lgdt [GDT64.Pointer]
jmp GDT64.Code:real_long_mode

[bits 64]
real_long_mode:
mov ax, GDT64.Data
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
call print_hello
jmp kernel
exit:
jmp exit
kernel:
;kernel code here

jmp exit

print_hello:
mov edi, 0xB8000              ; Set the destination index to 0xB8000.
mov rax, 0x1F201F201F201F20   ; Set the A-register to 0x1F201F201F201F20.
mov ecx, 500                  ; Set the C-register to 500.
rep stosq                     ; Clear the screen.

; Display "Hello World! AsmWave OS."
mov edi, 0x00b8000

mov rax, 0x1F6C1F6C1F651F48
mov [edi],rax

mov rax, 0x1F6F1F571F201F6F
mov [edi + 8], rax

mov rax, 0x1F211F641F6C1F72
mov [edi + 16], rax

mov rax, 0x1F6D1F731F411F20
mov [edi + 24], rax

mov rax, 0x1F651F761F611F57
mov [edi + 32], rax

mov rax, 0x1F2E1F531F4F1F20
mov [edi + 40], rax

ret

; Padding
times 510 - ($-$$) db 0

; Magic number
dw 0xaa55
